package com.hyzh.latte.sandtable.service;

import com.hyzh.latte.sandtable.pojo.Graph.PointNode;
import com.hyzh.latte.sandtable.pojo.Graph.TempConnect;
import com.hyzh.latte.sandtable.utils.VectorUtils;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Service;


import java.util.*;

@Service
public class GraphService {
    private final Neo4jClient neo4jClient;

    // 使用构造器注入Neo4jTemplate
    public GraphService(Neo4jClient neo4jClient) {
        this.neo4jClient = neo4jClient;
    }

    public void createRelationshipBetweenPoints(PointNode point1, PointNode point2, double distance, String area) {
        // 使用Neo4jClient构建并执行Cypher查询
        String cypherQuery = "MERGE (p1:PointNode {index: $index1, x: $x1, y: $y1, area: $area1}) " +
                "MERGE (p2:PointNode {index: $index2, x: $x2, y: $y2, area: $area2}) " +
                "MERGE (p1)-[r:CONNECT]->(p2) " +
                "SET r.distance = $distance " +
                "RETURN p1, p2, r ";
        Map<String, Object> map = new HashMap<>();
        map.put("index1", point1.getIndex());
        map.put("x1", point1.getPoint().getX());
        map.put("y1", point1.getPoint().getY());
        map.put("area1", area);
        map.put("index2", point2.getIndex());
        map.put("x2", point2.getPoint().getX());
        map.put("y2", point2.getPoint().getY());
        map.put("area2", area);
        map.put("distance", distance);
        map.put("area", area);

        neo4jClient.query(cypherQuery)
                .bindAll(map)
                .run();
    }

    public ArrayList<PointNode> findShortestPathBetweenPoints(TempConnect startTempConnect0, TempConnect startTempConnect1, TempConnect endTempConnect0, TempConnect endTempConnect1, String area) {
        ArrayList<PointNode> result = new ArrayList<>();
        //首先删除所有index为复数的节点
        String deleteQuery = "MATCH (n)-[r]-(m) " +
                "WHERE n.index < 0 OR  m.index < 0 " +
                "DELETE r " +
                "WITH DISTINCT n, m " +
                "MATCH (n) " +
                "WHERE n.index < 0 " +
                "DELETE n ";
        neo4jClient.query(deleteQuery)
                .run();

        String cypherQuery = "CALL apoc.create.node(['PointNode'], {index: -1, x: $x1, y: $y1, area: $area}) YIELD node as start " +
                " CALL apoc.create.node(['PointNode'], {index: -2, x: $x2, y: $y2, area: $area}) YIELD node as end " +
                "MATCH (startNode0:PointNode{index: $startIndex0, area: $area}) " +
                "MATCH (startNode1:PointNode{index: $startIndex1, area: $area}) " +
                "MATCH (endNode0:PointNode{index: $endIndex0, area: $area}) " +
                "MATCH (endNode1:PointNode{index: $endIndex1, area: $area}) " +
                "WITH  start, end, startNode0, startNode1, endNode0, endNode1 " +
                "CALL apoc.create.relationship(start, 'CONNECT', {distance: $startDistance0}, startNode0) YIELD rel as startRel0 " +
                "CALL apoc.create.relationship(start, 'CONNECT', {distance: $startDistance1}, startNode1) YIELD rel as startRel1 " +
                "CALL apoc.create.relationship(end, 'CONNECT', {distance: $endDistance0}, endNode0) YIELD rel as endRel0 " +
                "CALL apoc.create.relationship(end, 'CONNECT', {distance: $endDistance1}, endNode1) YIELD rel as endRel1 " +
                "CALL apoc.algo.aStar(start, end, 'CONNECT', 'distance', 'x', 'y') YIELD path   " +
                "UNWIND nodes(path) AS node " +
                "RETURN collect(DISTINCT node.index) AS indexes, collect(DISTINCT node.x) AS xs, collect(DISTINCT node.y) AS ys ";

        Map<String, Object> map = new HashMap<>();
        map.put("x1", startTempConnect0.getPoint().getX());
        map.put("y1", startTempConnect0.getPoint().getY());
        map.put("x2", endTempConnect0.getPoint().getX());
        map.put("y2", endTempConnect0.getPoint().getY());
        map.put("area", area);
        map.put("startIndex0", startTempConnect0.getIndex());
        map.put("startIndex1", startTempConnect1.getIndex());
        map.put("endIndex0", endTempConnect0.getIndex());
        map.put("endIndex1", endTempConnect1.getIndex());
        map.put("startDistance0", startTempConnect0.getDistance());
        map.put("startDistance1", startTempConnect1.getDistance());
        map.put("endDistance0", endTempConnect0.getDistance());
        map.put("endDistance1", endTempConnect1.getDistance());

        Collection<Map<String, Object>> resultList = neo4jClient.query(cypherQuery)
                .bindAll(map)
                .fetch()
                .all();
        neo4jClient.query(deleteQuery)
                .run();
        result = VectorUtils.graphResultToPointNode(resultList);
        return result;
    }

    public ArrayList<PointNode> findShortestPathByIndex(int startIndex, int endIndex, String area) {
        ArrayList<PointNode> result;

        String cypherQuery = "MATCH (start:PointNode {index: $startIndex,area: $area}) " +
                "MATCH (end:PointNode {index: $endIndex,area: $area}) " +
                "WITH start, end " +
                "CALL apoc.algo.aStar(start, end, 'CONNECT', 'distance', 'x', 'y') YIELD path " +
                "UNWIND nodes(path) AS node " +
                "RETURN collect(DISTINCT node.index) AS indexes, collect(DISTINCT node.x) AS xs, collect(DISTINCT node.y) AS ys ";

        Map<String, Object> map = new HashMap<>();
        map.put("startIndex", startIndex);
        map.put("endIndex", endIndex);
        map.put("area", area);

        Collection<Map<String, Object>> resultList = neo4jClient.query(cypherQuery)
                .bindAll(map)
                .fetch()
                .all();
        result = VectorUtils.graphResultToPointNode(resultList);
        return result;
    }


}